package maze.logic;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import maze.model.Cell;
import maze.model.Map;
import maze.model.Pair;
import maze.model.type.Face;

public class SmartLogic extends Logic
{
	protected Stack<State> path = new Stack<State>();

	@Override
	public void init()
	{
		calculateShortestPath();
	}

	@Override
	public void logic()
	{
		super.logic();

		if (!LogicManager.getInstance().moving)
		{
			// if the path is empty, calculate the shortest path
			if (path.isEmpty())
			{
				calculateShortestPath();
			}

			// fetch the first state in the path, and move
			State state = path.pop();
			move(state.face);
		}
	}

	/**
	 * calculate the shortest path to move to the target position
	 */
	private void calculateShortestPath()
	{
		path.clear();

		Cell[][] cells = Map.getInstance().cells;

		// the new state is fetched from the list at the open index
		int open = -1;

		// the state is added to the list at the closed index
		int closed = 0;

		// the state list
		List<State> states = new ArrayList<State>();

		// add the original state
		states.add(new State(sun.pos, -1, null));

		// the state at index closed is to be added to the list,
		// the state at index open is to be fetched from the list,
		// so when open==closed, there's no state available in the list for processing
		while (open < closed)
		{
			// fetch the next state in the list
			State state = states.get(++open);
			Cell cell = cells[state.pos.x][state.pos.y];

			// try different faces to move
			for (Face face : Face.values())
			{
				if (cell.canWallPass(face))
				{
					// find a new state
					Pair newPos = cell.adjacentCellPosition(face);

					// check if the new state already exists
					State newState = new State(newPos, open, face);
					if (!states.contains(newState))
					{
						++closed;
						states.add(newState);
					}

					// check if the new state if the final state
					if (newState.pos.equals(target.pos))
					{
						// add the states to the solution path
						int t = closed;
						System.out.println("----------------");

						while (t > 0)
						{
							State s = states.get(t);
							path.push(s);
							System.out.println("id=" + t + " " + s);
							t = s.from;
						}

						// one solution is enough
						return;
					}
				}
			}
		}

		if (open >= closed)
		{
			// no solution - ignore,
			// since this would never happen in this case
		}
	}
}

class State
{
	public Pair pos;
	public int from;
	public Face face;

	public State(Pair pos, int from, Face face)
	{
		this.pos = pos;
		this.from = from;
		this.face = face;
	}

	@Override
	public boolean equals(Object obj)
	{
		if (obj == null || !(obj instanceof State))
			return false;

		State that = (State) obj;

		// only the position that matters
		return pos.equals(that.pos);
	}

	@Override
	public String toString()
	{
		return "[STATE] " + pos + ",from=" + from + ",face=" + face;
	}
}
